package org.example.seata.bs.auth.handler.code;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.Data;
import org.example.seata.bs.api.entity.account.AccountAuth;
import org.example.seata.bs.api.service.account.AccountAuthService;
import org.example.seata.common.core.util.ApiResp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ValueOperations;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * @author hzq
 * @date 2021/12/23 13:21
 */
@Data
public abstract class AbstractSendCodeHandler implements SendCodeHandler {

    @Autowired
    protected AccountAuthService accountAuthService;
    @Autowired
    protected ValueOperations valueOperations;

    @Override
    public ApiResp<Object> sendCode(String principal, String handlerType) {

        // 一分钟内只能发送一条验证码信息
        Object code = valueOperations.get(generateKey(principal, handlerType));
        if (code instanceof Code) {
            LocalDateTime sendTime = ((Code) code).getSendTime();
            if (LocalDateTime.now().isBefore(sendTime.plusSeconds(60))) {
                return ApiResp.ofErrorMessage("验证码发送频繁");
            }
        }

        // 判断系统中是否有该用户信息
        AccountAuth auth = getAuth(principal, handlerType);
        if (auth == null) {
            return ApiResp.ofErrorMessage("发送验证码失败，用户信息有误");
        }

        return handler(principal, handlerType);
    }

    /**
     * @param principal   标识
     * @param handlerType 类型
     * @return redisKey
     */
    public static String generateKey(String principal, String handlerType) {
        return String.format("send_code:login:%s:%s", handlerType, principal);
    }

    /**
     * 保存验证码到redis
     *
     * @return 验证码信息
     */
    protected Code saveCodeToRedis(String principal, String handlerType) {

        // 生成验证码信息
        Code code = createCode();

        // 保存验证码信息 验证码10分钟过期
        valueOperations.set(generateKey(principal, handlerType), code, 10, TimeUnit.MINUTES);

        return code;
    }

    /**
     * 获取用户信息
     *
     * @param principal   标识
     * @param handlerType 类型
     * @return 用户信息
     */
    protected AccountAuth getAuth(String principal, String handlerType) {

        LambdaQueryWrapper<AccountAuth> wrapper = Wrappers.<AccountAuth>lambdaQuery()
                .eq(AccountAuth::getAuthType, handlerType)
                .eq(AccountAuth::getPrincipal, principal);

        return getAccountAuthService().getOne(wrapper);
    }

    /**
     * @return 是否支持发送该类型验证码
     */
    protected abstract boolean supportType(String handlerType);

    /**
     * 发送验证码
     *
     * @param principal   发送目标
     * @param handlerType 发送目标
     * @return 发送信息
     */
    protected abstract ApiResp<Object> handler(String principal, String handlerType);

    /**
     * @return 生成验证码
     */
    protected Code createCode() {
        Code code = new Code();
        code.setCode(RandomUtil.randomString(RandomUtil.BASE_NUMBER, 6));
        code.setSendTime(LocalDateTime.now());
        return code;
    }

}
